#include "opencv/cv.h"
#include "opencv/highgui.h"
#include <stdio.h>
#include <stdlib.h>

#define FPS 15
#define forn(i,n) for(i=0;i<n;i++)

extern void asmRoberts(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmSobel(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmPrewitt(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmMono(const char* src, char* dst, int ancho, int alto);

extern void merge(const char* src1, const char* src2, char* dst, int ancho, int alto);
extern void suma(const char* src1, const char* src2, char* dst, int ancho, int alto);
extern void andFilter(const char* src1, const char* src2, char* dst, int ancho, int alto);
extern void nandFilter(const char* src1, const char* src2, char* dst, int ancho, int alto);

int main( int argc, char** argv ){
   IplImage * src = 0;
   IplImage * dst = 0;

   char* filename = argc==3?argv[2]:(char*)"lena.bmp";

	if(argc>=2 && strcmp(argv[1],"webcam")){
		if((src = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE))==0) return -1;
		if((dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1))==0) return -1;
		
		if(!strcmp(argv[1],"r1") || !strcmp(argv[1],"roberts")){
			asmRoberts(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("roberts.bmp", dst);
			printf("%s\n","output written to roberts.bmp");
		
		}else if(!strcmp(argv[1],"r2") || !strcmp(argv[1],"prewitt")){
			asmPrewitt(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("prewitt.bmp", dst);
			printf("%s\n","output written to prewitt.bmp");

		}else if(!strcmp(argv[1],"r3") || !strcmp(argv[1],"sobelx")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 0);
			cvSaveImage("sobel_x.bmp", dst);
			printf("%s\n","output written to sobel_x.bmp");
			
		}else if(!strcmp(argv[1],"r4") || !strcmp(argv[1],"sobely")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 0, 1);
			cvSaveImage("sobel_y.bmp", dst);
			printf("%s\n","output written to sobel_y.bmp");
			
		}else if(!strcmp(argv[1],"r5") || !strcmp(argv[1],"sobel")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("sobel.bmp", dst);
			printf("%s\n","output written to sobel.bmp");

		}else if(!strcmp(argv[1],"mono")){
			asmMono(src->imageData, dst->imageData, src->width, src->height);
			cvSaveImage("mono.jpg", dst);
			printf("%s\n","output written to mono.jpg");

		}else if(!strcmp(argv[1],"time")){
			int it=1000;
			printf("tomando tiempos de asmSobel y cvSobel con %d iteraciones...\n\n",it);
			int tscl;
			long long int totasm=0;
			unsigned int minasm=0;
			int i;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 0);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totasm += tscl;
				minasm=!minasm||tscl<minasm?tscl:minasm;
			}
			//cvSaveImage("sobel_x.bmp", dst);
			//printf("%s\n","output written to sobel_x.bmp");
			printf("asmSobel:\nPromedio: %.0f\nMinimo: %d\n",(double)totasm/it,minasm);

			/* Ahora la lib openCv */
			printf("\n");
			long long int totcv=0;
			unsigned int mincv=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				cvSobel(src, dst, 1, 0, 3);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totcv += tscl;
				mincv=!mincv||tscl<mincv?tscl:mincv;
			}
			/* Imprimo los resultados */
			//cvSaveImage("sobel_openCv_x.bmp", dst);
			//printf("%s\n","output written to sobel_openCv_x.bmp");
			printf("cvSobel:\nPromedio: %.0f\nMinimo: %d\n",(double)totcv/it,mincv);
			printf("\n");
			printf("asmSobel/cvSobel:  %.2f\n",(double)totasm/totcv);
		}
	}else if(argc>=2 && !strcmp(argv[1],"webcam")){
		/* Esto es algo aparte que implementamos. Aplica los filtros en tiempo real a la entrada de la webcam (de haber una) */
		cvNamedWindow("camara",1);
		CvCapture* capture = (argc==3)?cvCaptureFromAVI(filename):cvCaptureFromCAM(-1);
		if(!capture){printf("%s\n","Error al conectar la camara");return -1;}
		IplImage* frame = cvQueryFrame(capture);
		if((src = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
		if((dst = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
		cvMoveWindow("camara",100,100);

		while(1){
			frame = cvQueryFrame(capture);
			cvCvtColor(frame,src,CV_BGR2GRAY); 										// convierte RGB a Grayscale
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1); // aplico la mascara de sobel
			cvShowImage("camara",dst);
			int key = cvWaitKey(1000/FPS);
			if((char)key==27) break;
			else if((char)key=='p'){cvSaveImage("webcam_screenshot.jpg", dst); printf("%s\n","output written to webcam_screenshot.jpg");}
		}
		cvReleaseCapture(&capture);
		cvDestroyWindow("camara");
	}else{
		if((src = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE))==0) return -1;
		if((dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1))==0) return -1;
		asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1);
		cvSaveImage("sobel.bmp", dst);
		printf("%s\n","output written to sobel.bmp");
	}
   return 0;
}
